home *** CD-ROM | disk | FTP | other *** search
/ Internet Publisher's Toolbox 2.0 / Internet Publisher's Toolbox.iso / internet / ntserver / wtsource / irsearch.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-15  |  46.2 KB  |  1,455 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.    
  4.    Brewster@think.com
  5. */
  6.  
  7. /* Copyright (c) CNIDR (see ../COPYRIGHT) */
  8.  
  9.  
  10. /* Looks up words in the inverted file index.
  11.  *
  12.  * Important functions:
  13.  * run_search
  14.  * search_for_words
  15.  *
  16.  * to do:
  17.  *    Handle searches on multiple databases
  18.  */
  19.  
  20. /* Change Log:
  21.  * $Log: irsearch.c,v $
  22.  * Revision 1.5  1993/10/17  15:38:50  huynh1
  23.  * new code added for soundex, phonix retrieval and
  24.  * nested boolean queries.
  25.  *
  26.  * Revision 1.3  93/07/21  18:46:35  warnock
  27.  * Added STELAR-specific patches
  28.  * 
  29.  * Revision 1.2  93/07/02  18:04:26  warnock
  30.  * replace handle_next_and_previous for multi-type from francois
  31.  * 
  32.  * Revision 1.1  1993/02/16  15:05:35  freewais
  33.  * Initial revision
  34.  *
  35.  * Revision 1.54  92/05/10  14:44:35  jonathan
  36.  * Made a little safer on NULL docid's when parsing.
  37.  * 
  38.  * Revision 1.53  92/05/04  17:20:11  jonathan
  39.  * Added test for parsing docids (if null, log error).
  40.  * 
  41.  * Revision 1.52  92/04/29  08:22:17  shen
  42.  * declare global variable "_BE_normalized" to allow turning on/off FE score
  43.  * normalization.
  44.  * 
  45.  * Revision 1.51  92/04/28  16:56:30  morris
  46.  * added boolean to serial engine
  47.  * 
  48.  * Revision 1.50  92/04/01  17:10:21  jonathan
  49.  * ?
  50.  * 
  51.  * Revision 1.49  92/03/23  13:26:27  shen
  52.  * add timing for query. Compile with GET_QUERY_TIMING. print timing every 200 queries.
  53.  * 
  54.  * Revision 1.48  92/03/18  08:56:00  jonathan
  55.  * Removed databaseName argument to getDocumentText and getData.
  56.  * 
  57.  * Revision 1.47  92/02/17  16:22:42  jonathan
  58.  * Added WCAT to types that can be used for relevance feedback.
  59.  * 
  60.  * Revision 1.46  92/02/16  18:04:38  jonathan
  61.  * Demoted more WLOG_ERROR's to WLOG_WARNING's
  62.  * 
  63.  * Revision 1.45  92/02/16  09:51:12  jonathan
  64.  * Plugged some memory leaks.  I be there are more.
  65.  * 
  66.  * Revision 1.44  92/02/15  19:41:20  jonathan
  67.  * Improved logging for invalid relevant documents.
  68.  * 
  69.  * Revision 1.43  92/02/14  16:06:48  jonathan
  70.  * Added diagnostic record for invalid relevant document.
  71.  * 
  72.  * Revision 1.42  92/02/12  17:30:20  jonathan
  73.  * Conditionalized inclusion of object code.
  74.  * 
  75.  * Revision 1.41  92/02/12  17:04:03  jonathan
  76.  * Moved logging info around.
  77.  * 
  78.  * Revision 1.40  92/02/12  15:26:35  morris
  79.  * only call fnished_search_word when the preceeding search was successful
  80.  * 
  81.  * Revision 1.39  92/02/12  13:30:39  jonathan
  82.  * Added "$Log" so RCS will put the log message in the header
  83.  * 
  84.  * changes 5.2.90 HWM
  85.         - changed calls to perror() to calls to panic()
  86.         - made print_best_hits() only print hits w/ non-zero weight
  87.         - made random arrays static instead of reading them in.  
  88.           removed getRandomArray.
  89.         - removed unused variables
  90.   Brewster 7/90 made look_up_word_in_dictionary safer.
  91.   Brewster 7/90 elimiated trailing <lf> on filename and headline table accesses
  92.   HWM 7.12.90 - replaced all calls to panic with error code returns and a log
  93.                 file  
  94.               - added the routine initSearchEngine() which should be called 
  95.                 before any other search routine
  96.               - added beFriendly() to give other processes time under 
  97.                 multifinder
  98.   JG 5.31.91 - added relevance feedback for line fragments.
  99.   JG 7.8.91  - added doc_id to search_for_words, removed scale_scores.
  100. */
  101.  
  102. #if 0
  103. #define GET_QUERY_TIMING
  104. #endif
  105.  
  106. #define _search_c
  107.  
  108. #ifdef __osf__
  109. #include <ctype.h>
  110. #endif /* ndef __osf__ */
  111.  
  112. #include <string.h>     /* for strlen() */
  113. #ifdef THINK_C
  114. #include <unix.h>               /* for sleep() */
  115. #endif /* think_c */
  116.  
  117. #include "cutil.h"
  118. #include "irfiles.h"
  119. #include "irtfiles.h" /* for map_over_words */
  120. #include "irlex.h"
  121. #include "irext.h"
  122. #include "irsearch.h"
  123. #include "docid.h"
  124. #include <math.h>
  125. #include "irretrvl.h"
  126. #ifdef BOOL
  127. #include "irparse.h"
  128. #endif
  129. #ifdef SOUND
  130. #include "soundex.h"
  131. #endif
  132.  
  133. /* tung , 10/93 */
  134. #ifdef BOOLEANS
  135. #include "manipulstr.h"
  136. #endif
  137. /* tung */
  138.  
  139. #include "trie.h"
  140.  
  141. #ifdef WIN32
  142. #include <ctype.h>
  143. void dispose_trie_allocator(trie_allocator*);
  144. long previous_docid(char*,database*); 
  145. long next_docid(char*,database*);
  146. int encode(unsigned char*);
  147. void decode(unsigned char*);
  148. void panic(char*,...);
  149. #endif
  150.  
  151. #define TEST_SEARCH     false   /* set to TRUE to allow printing to console */
  152.  
  153. #ifdef STELAR
  154. /* File of associations between STELAR abstracts and bitmaps */
  155. FILE *BITMAPS;
  156. #endif /* STELAR */
  157.  
  158. char *server_name = NULL;
  159. long tcp_port = 0;
  160.  
  161. long _BE_normalized = 0;
  162.  
  163. #ifdef GET_QUERY_TIMING
  164. #include <sys/timeb.h>
  165. static struct timeb  s_time, e_time;
  166. static float t_time = 0;
  167. static long n_query = 0;
  168. #endif
  169.  
  170.  
  171. /*----------------------------------------------------------------------*/
  172.  
  173. static Boolean calcDocLength _AP((hit* theHit,long* lines,long* bytes));
  174.  
  175. static Boolean
  176. calcDocLength(theHit,lines,bytes)
  177. hit* theHit;
  178. long* lines;
  179. long* bytes;
  180. /* Given a hit, open the file and figure out how many bytes and lines
  181.    it contains.  This is not needed by the serial search engine (it
  182.    stores these values in its dictionary.  It is used by the dynamic
  183.    help facility).
  184. */
  185. {
  186.   *lines = theHit->number_of_lines;
  187.  
  188.   /* find the length of the document */
  189.   if(theHit->end_character != 0)
  190.     {
  191.       /* document is not whole file, so size is stored */
  192.       *bytes = theHit->end_character - theHit->start_character;
  193.       return(true);
  194.     }
  195.   else
  196.     {   
  197.       /* whole file, find file length from the file */
  198.       FILE* file = NULL;
  199.       if (((file = s_fopen(theHit->filename, "r")) != NULL) &&
  200.           (s_fseek(file, 0L, SEEK_END) == 0)  &&
  201.           ((*bytes = ftell(file)) != -1))
  202.         { s_fclose(file);
  203.           return(true);         /* we are done, bytes is set */
  204.         }
  205.       else
  206.         { s_fclose(file);
  207.           return(false);        /* something went wrong with the file */
  208.         }
  209.     }
  210. }
  211.  
  212.  
  213.  
  214. static long search_word_before_pairs _AP((char *word, long char_pos,
  215.                                        long line_pos, long weight,
  216.                                        long doc_id, time_t doc_date,
  217.                                        boolean capitalized, database* db));
  218.  
  219. /* returns 0 is successful, non-0 if error.  A copy of add_word_before_pairs */
  220. static long search_word_before_pairs (word, char_pos, line_pos,
  221.                                       weight, doc_id, doc_date, capitalized, db)
  222.      char *word;        /* the word to be indexed, this could be a
  223.                            word pair. If NULL there are no more words
  224.                            to be indexed */
  225.      long char_pos;     /* the position of the start of the
  226.                            word */
  227.      long line_pos;     /* this is passed for the best
  228.                            section calculation */
  229.      long weight;       /* how important the word looks
  230.                            syntactically (such as is it bold)
  231.                            NOT used by signature system */
  232.      long doc_id;       /* current document, this will never be 0 */
  233.      time_t doc_date; /* display day of this document, 0 if not known */
  234.      boolean capitalized; /* if the word started with a cap */
  235.      database* db; /* database to insert the document */
  236. {
  237.   static char last_word[MAX_WORD_LENGTH + 1];
  238.   static long last_doc_id = -1;
  239.   /* The way it works is it remembers if the last word if it was
  240.      capitalized (if not it clears the saved word).  
  241.      If another capitalized word comes along next
  242.      (and it is in the same document), then it makes a joint word and calls 
  243.      add_word with it. */
  244.   if(capitalized){
  245.     if(last_word[0] != '\0' && last_doc_id == doc_id){
  246.       search_word(make_joint_word(last_word, word), 
  247.                   char_pos, line_pos, weight, doc_id, 1L, db);
  248.     }
  249.     else{
  250.       last_word[0] = '\0';
  251.     }
  252.     strncpy(last_word, word, MAX_WORD_LENGTH);
  253.     last_doc_id = doc_id;
  254.   }
  255.   else{ /* not capitalized */
  256.     last_word[0] = '\0';
  257.   }
  258.   return(search_word(word, char_pos, 
  259.                      line_pos, weight, doc_id, 0L, db));
  260. }
  261.  
  262. long count_trie_words;
  263. long count_uniq;
  264.  
  265. boolean prepare_word_list(words,set,alloc)
  266.      char* words;
  267.      trie* set;
  268.      trie_allocator* alloc;
  269. {
  270.   char* word = NULL;
  271.   int * datum;
  272.   count_trie_words = count_uniq = 0;
  273.   /* printf("words: %s\n", words); */
  274. #ifdef BIO
  275.   word = (char*)strtokf(words,wordDelimiter);
  276. #else
  277.   word = (char*)strtokf_isalnum(words);
  278. #endif
  279.   while(word != NULL){
  280. #ifndef WIN32
  281.     long dictionary_value;
  282. #endif
  283.     /* trim the string if necessary */
  284.     if(strlen(word) > MAX_WORD_LENGTH){
  285.       word[MAX_WORD_LENGTH] = '\0';
  286.     }
  287.  
  288.     if(!encode((unsigned char*)word)) {
  289.       panic("can't encode word %s",word);
  290.     }
  291.     datum = (int *)trie_lookup(word,set,alloc);
  292.     if(!datum) {
  293.       panic("trie_lookup failed !!!");
  294.     }
  295.  
  296.       count_trie_words++;
  297.  
  298.     *datum += 1;
  299.  
  300.       if (*datum == 1 ) {
  301.       count_uniq++;
  302.       }
  303. #ifdef BIO
  304.     word = (char *)strtokf(NULL,wordDelimiter);
  305. #else
  306.     word = (char *)strtokf_isalnum(NULL);
  307. #endif
  308.     beFriendly();
  309.   } 
  310.  
  311.   waislog(WLOG_LOW, WLOG_INFO,
  312.           "after preparing word list, %d search items were presented.",
  313.           count_trie_words);
  314.   waislog(WLOG_LOW, WLOG_INFO, 
  315.           "There are %d words to search for.",
  316.           count_uniq);
  317.  
  318.   return(true);
  319. }
  320.  
  321. boolean search_for_trie_words(dict,db,prefix,docid,result)
  322. trie* dict;
  323. database* db;
  324. char* prefix;
  325. long docid;
  326. boolean result;
  327. {
  328.   char buffer[MAX_WORD_LENGTH+1];
  329.   char tmp_word[MAX_WORD_LENGTH+1];
  330.   char* word;
  331. #ifndef WIN32
  332.   long dictionary_value;
  333.   int weight;
  334. #endif
  335.   char* tmp=word;
  336.   if (dict == NULL) {
  337.     return result;
  338.   }
  339.   if (*dict->string) {
  340.     strcpy(buffer,prefix);
  341.     strcat(buffer,dict->string);
  342.     word = buffer;
  343.   } else {
  344.     word = prefix;
  345.   }
  346.  
  347.   if (dict->datum) {
  348. #ifndef WIN32
  349.     long number_of_occurrences;
  350. #endif
  351.     /* this node has data */
  352.     strcpy(tmp_word,word);
  353.     decode(tmp_word);
  354.     result |= search_word(tmp_word,0L,0L,1L,docid,0L,db);
  355.   }
  356.   if (dict->table) {
  357.     int i;
  358.     int len;
  359.     len = strlen(word);
  360.     for (i=0;i<ALPHA_SIZE;i++) {
  361.       if(dict->table[i]) {
  362.         word[len]=(char)i;
  363.         word[len+1]='\0';
  364.         result = search_for_trie_words(dict->table[i],db,word,docid,result);
  365.       }
  366.     }
  367.   }
  368.   return result;
  369. }
  370.  
  371.  
  372. /* dgg -- pulled this from irtfiles.c:map_over_words */
  373. /* returns the number of words added, or -1 if an error occurred */
  374. long search_over_words
  375.   _AP((char* line,long document_id,database* db,char* words_used));
  376.        
  377. long search_over_words(line, document_id, db, words_used) 
  378. char* line;
  379. long document_id;
  380. database* db;
  381. char* words_used;
  382. {
  383.   long weight = 1L;
  384.   long file_position_before_line = 0;
  385.   boolean word_position= false;
  386.   boolean word_pairs= false;
  387. #ifdef BIO
  388.   int minwordlen= 1; /* only if symbols are active ? */
  389. #else
  390.   int minwordlen= 2;
  391. #endif
  392.  
  393.   long position_in_word = 0;
  394.   long word_count = 0;
  395.   unsigned long ch;
  396.   long char_count = 0;
  397.   boolean capitalized = false; /* if the word starts with a cap */
  398. #ifdef LITERAL
  399.   char  word[MAX_PHRASE_LENGTH + 1];
  400.   char  key;
  401. #else
  402.   char word[MAX_WORD_LENGTH + 1];
  403. #endif  
  404. #ifdef BOOLEANS
  405. #define MAX_LINE_LENGTH 1000  
  406.   boolean  nextIsNot = false;
  407.   char  notwords[MAX_LINE_LENGTH+1];
  408.   boolean parentheses = false; /* 10/93, tung */
  409. #endif
  410. #ifdef SOUND
  411.   boolean nextIsSoundex = false;
  412.   boolean nextIsPhonix = false;
  413. #endif
  414.  
  415. #ifdef BOOLEANS
  416.   notwords[0]= '\0';
  417.  
  418. /* tung, 10/93 
  419.      for boolean search with parentheses "(... and ... )".
  420.      There may but need not be blanks (or tabs) around the parentheses.
  421.      */
  422.   if((strchr(line, '(' )) && (strchr(line, ')' ))) {
  423.     parentheses = true;
  424.     line = string_manipulation(line);
  425.     minwordlen = 1; /* e.g. for query (a or information) */
  426.   }
  427. /* tung, 10/93 */
  428. #endif
  429.  
  430.  
  431.   for(ch = (unsigned char)line[char_count++]; 
  432.       ch != '\0'; ch = (unsigned char)line[char_count++]){
  433. #ifdef BIO
  434.     boolean alnum = (wordDelimiter(ch) == NOT_DELIMITER);
  435. #else
  436.     boolean alnum = isalnum(ch);
  437. #endif
  438. #ifdef PARTIALWORD
  439.     if (ch == PARTWORD_WILDCARD) { alnum= true; minwordlen= MAX(2,minwordlen); }
  440. #endif
  441. #ifdef LITERAL
  442.    if (ch == LITERAL_KEY1)  key= LITERAL_KEY1;
  443.    else if (ch == LITERAL_KEY2)  key= LITERAL_KEY2;
  444.    else { key= 0; weight = 1L ;} /* by only key=0 is it not possible
  445.                                     combine literal search and boolean search
  446.                                   */
  447.    if (key != 0) {
  448.       char *cp, *match;
  449.       cp = line + char_count;  
  450.       match = strchr( cp, key); 
  451. /* printf("search_over_words: literal key is [%c]\n", key); */
  452.       if (match != NULL && cp < match) {
  453.           for (position_in_word=0; cp < match; cp++, char_count++)
  454.             if (position_in_word < MAX_PHRASE_LENGTH) {
  455.              word[position_in_word++] = char_downcase((unsigned long)*cp);
  456.              }
  457.           char_count++; /* skip closing key */
  458.           alnum= false;
  459.           capitalized= false;
  460.           weight= LITERAL_FLAG;   /* is this a safe flag parameter? -- 
  461.                 unused but passed on to search_word is what we need */
  462.         /* !! need to break literal "word" into 1st dictionary word and search
  463.                 on that... */
  464. /* printf("search_over_words: literal is [%s]\n", word); */
  465.         }
  466.       }
  467. #endif
  468.  
  469.     if(alnum){
  470.       /* put the character in the word if not too long */
  471.       if(position_in_word == 0)
  472.         capitalized = isupper((unsigned long)ch)?true:false;
  473.       if(position_in_word < MAX_WORD_LENGTH){
  474.         word[position_in_word++] = char_downcase((unsigned long)ch);
  475.       }
  476.     }
  477.     else{ /* not an in a word */
  478.       if(position_in_word != 0){
  479. #ifdef BOOLEANS
  480. /* note on BOOLEANS -- we really need to check for NOT words here,
  481.    and move them to back of line so that (wordfunction)== search_word is
  482.    called for NOT words after other words (excluding NOT inside a literal)
  483. */
  484.         if (nextIsNot) {
  485.           word[position_in_word] = '\0';
  486.           strcat( notwords, word);
  487.           strcat( notwords, " ");
  488.           nextIsNot= false;
  489.           word_count++;
  490.           }
  491.         else if ((strncmp(word,BOOLEAN_NOT,position_in_word)==0)) {
  492.           if(parentheses == false) {  /* tung, 10/93 */
  493.             nextIsNot= true;
  494.             word_count++;
  495.           }
  496.           else {                      /* tung, 10/93 */
  497.             word[position_in_word] = '\0';
  498.             if(0 != search_word_before_pairs
  499.                (word,
  500.                 file_position_before_line + char_count,  
  501.                 /*^^  this param is supposed to be start-of-word, but char_count is now at end-of-word !*/
  502.                 0L, /* line_pos */
  503.                 weight, 
  504.                 document_id, 
  505.                 (time_t)0L,
  506.                 capitalized,
  507.                 db))  
  508.               return(-1); /* error */
  509.             nextIsNot= false;
  510.             word_count++;
  511.           }
  512.         }
  513.         else
  514. #endif
  515. #ifdef SOUND
  516.           if (nextIsSoundex) {
  517.             static char Key[80];
  518.             word[position_in_word] = '\0';
  519.             SoundexCode(word,Key);
  520. #ifdef DEBUG
  521.             printf("search_over_words: adding Soundex (%s,%s)\n", word, Key);
  522. #endif
  523.             if(0 != search_word_before_pairs
  524.                (Key,
  525.                 file_position_before_line + char_count, 
  526.                 0L,     /* line_pos */
  527.                 weight, 
  528.                 document_id, 
  529.                 (time_t)0L,
  530.                 capitalized,
  531.                 db))
  532.               return(-1);
  533.             word_count++; nextIsSoundex = false;
  534.           } else if ((strncmp(word,SOUNDEX,strlen(SOUNDEX))==0)) {
  535.             nextIsSoundex= true;
  536.             word_count++;
  537.           }
  538.           else if (nextIsPhonix) {
  539.             char Key[80];
  540.             word[position_in_word] = '\0';
  541.             PhonixCode(word,Key);
  542. #ifdef DEBUG
  543.             printf("search_over_words: adding Phonix (%s,%s)\n", word, Key);
  544. #endif
  545.             if(0 != search_word_before_pairs
  546.                (Key,
  547.                 file_position_before_line + char_count, 
  548.                 0L,     /* line_pos */
  549.                 weight, 
  550.                 document_id, 
  551.                 (time_t)0L,
  552.                 capitalized,
  553.                 db))
  554.               return(-1);
  555.             word_count++; nextIsPhonix = false;
  556.           } else if ((strncmp(word,PHONIX,strlen(PHONIX))==0)) {
  557.             nextIsPhonix= true;
  558.             word_count++;
  559.           } else
  560. #endif
  561.         /* then we have collected a word */
  562.         if(position_in_word >= minwordlen){ /* is it reasonable ? */
  563.           long result;
  564.           word[position_in_word] = '\0';
  565.           if(0 > (result = 
  566.                   search_word_before_pairs(word,
  567.                              file_position_before_line + char_count,  
  568.                              /*^^  this param is supposed to be start-of-word, but char_count is now at end-of-word !*/
  569.                              0L, /* line_pos */
  570.                              weight, 
  571.                              document_id, 
  572.                              (time_t)0L,
  573.                              capitalized,
  574.                              db)))
  575.             return(-1); /* error */
  576.             if(result == 1 && words_used != NULL) {
  577.             strcat(words_used, word);
  578.             strcat(words_used, " ");
  579.           }
  580. #ifdef BOOLEANS
  581.           nextIsNot= false;
  582. #endif
  583. #ifdef SOUND
  584.               nextIsPhonix = false;
  585.               nextIsSoundex = false;
  586. #endif
  587.           word_count++;
  588.         }
  589.         position_in_word = 0;
  590.       }
  591.     }
  592.   }
  593.   
  594.   /* finish last word */
  595.  
  596. #ifdef BOOLEANS
  597.   if (nextIsNot) {
  598.     word[position_in_word] = '\0';
  599.     strcat( notwords, word);
  600.     strcat( notwords, " ");
  601.     nextIsNot= false;
  602.     word_count++;
  603.     }
  604.  else 
  605. #endif
  606. #ifdef SOUND
  607.     if (nextIsSoundex) {
  608.     static char Key[80];
  609.     long result;
  610.     word[position_in_word] = '\0';
  611.     SoundexCode(word,Key);
  612. #ifdef DEBUG
  613.     printf("search_over_words: adding Soundex (%s,%s)\n", word, Key);
  614. #endif
  615.     if(0 > (result = 
  616.             search_word_before_pairs(Key,
  617.                                      file_position_before_line + char_count, 
  618.                                      0L,        /* line_pos */
  619.                                      weight, 
  620.                                      document_id, 
  621.                                      (time_t)0L,
  622.                                      capitalized,
  623.                                      db)))
  624.       return(-1);
  625.     word_count++;
  626.     }
  627.     else if (nextIsPhonix) {
  628.       char Key[80];
  629.       long result;
  630.       word[position_in_word] = '\0';
  631.       PhonixCode(word,Key);
  632. #ifdef DEBUG
  633.       printf("search_over_words: adding Phonix (%s,%s)\n", word, Key);
  634. #endif
  635.       if(0 > (result = 
  636.               search_word_before_pairs(Key,
  637.                                        file_position_before_line + char_count, 
  638.                                        0L,      /* line_pos */
  639.                                        weight, 
  640.                                        document_id, 
  641.                                        (time_t)0L,
  642.                                        capitalized,
  643.                                        db)))
  644.       return(-1);
  645.       if (result == 1 && words_used != NULL) {
  646.         strcat(words_used, word);
  647.         strcat(words_used, " ");
  648.       }
  649.       word_count++;
  650.     }
  651.   else
  652. #endif
  653.  if(position_in_word >= minwordlen){ /* is it reasonable ? */
  654.     word[position_in_word] = '\0';
  655.     if(0 > search_word_before_pairs(word,
  656.                             file_position_before_line + char_count, 
  657.                             0L, /* line_pos */
  658.                             weight, 
  659.                             document_id, 
  660.                             (time_t)0L,
  661.                             capitalized,
  662.                             db))  
  663.       return(-1);
  664.     word_count++;
  665.   }
  666.   
  667. #ifdef BOOLEANS
  668.   if ((notwords[0] != '\0')) { 
  669.     char wordn[MAX_WORD_LENGTH+1];   /* this is a copy of wordp */
  670.     char  *wordp;
  671.     capitalized= false;
  672.     char_count= 0;       /* !?? need char index for each word ? */
  673.     weight= BOOLEAN_NOT_FLAG; /* is this a safe parameter ? */   
  674.     wordp= strtok( notwords, " ");
  675.     while (wordp!=NULL) {
  676.       s_strncpy(wordn,wordp,MAX_WORD_LENGTH);
  677.       if(0 > 
  678.          search_word_before_pairs(wordn,
  679.                                        file_position_before_line + char_count, 
  680.                                        0L,      /* line_pos */
  681.                                        weight, 
  682.                                        document_id, 
  683.                                        (time_t)0L,
  684.                                        capitalized,
  685.                                        db))  
  686.         return(-1);
  687.       wordp= strtok(NULL, " ");
  688.     }
  689.   }
  690. #endif
  691.   return(word_count);
  692. }
  693.  
  694.  
  695. boolean search_for_words(words, db, doc_id, words_used)
  696.      char* words;
  697.      /* break the string into words (using map_over_words)
  698.         and repeatedly call search_word_before_pairs(). 
  699.         Returns true if successful.
  700.         */
  701.      database *db;
  702.      long doc_id;  /* = 1 for words == document in relevance feedback search */
  703.      char   *words_used;
  704. {
  705.  
  706. #ifdef BOOL
  707.   /* LISP QUERY */
  708.   if( words[0] == '(' ){ /* then it is a lisp query */
  709.     /* this is a temporary stub for the real work */
  710.     char error_string[ERROR_STRING_LEN];
  711.     object* query = (object*)parseQuery(words,QUERY_SYNTAX_LISP,error_string);
  712.     if(query == NULL){
  713.       waislog(WLOG_HIGH, WLOG_ERROR, "Unparsable query %s", error_string);
  714.       return(false);
  715.     }
  716.     else{
  717.       query = (object*)send(query,Evaluate,db);
  718.       return(true);
  719.     }
  720.   }
  721. #endif  /* def BOOL */
  722.  
  723. /* dgg mods really need new version of map_over_words for searching only
  724.   (not for adding == indexing), and this way we can keep main search
  725.   routines here (irsearch.c) & search_word in sersrch.c
  726. */
  727.  
  728.   /* NORMAL QUERY */
  729.   if( -1 == search_over_words(words, doc_id, db, words_used))
  730.     return(false);
  731.   else
  732.     return(true); 
  733. }
  734.  
  735.  
  736. /* gets the next best hit from the search engine and fills in all the slots.
  737.    If the document does not exist, then it gets another, etc.
  738.    It returns 0 if successful */   
  739. long next_best_hit(the_best_hit, db)
  740.      hit *the_best_hit;
  741.      database *db;
  742. {
  743.   document_table_entry doc_entry;
  744.   long ret_value,start,end,length,date,nlines=0;
  745.   char headline[100];
  746.   char filename[200],type[20];
  747.   while(1){ /* keep going until we get a good document */
  748.     if(0 != (ret_value = best_hit(db,&(the_best_hit->document_id),
  749.                                   &(the_best_hit->best_character),
  750.                                   &(the_best_hit->best_line),
  751.                                   &(the_best_hit->weight),
  752.                                   &start,&end,&date,&length,&nlines,
  753.                                   headline,filename,type))){
  754.       /* if we add start,end,length,date,headline here, we can remove lots of
  755.          disk reads */
  756.       
  757.       return(ret_value);
  758.     }
  759.     if(the_best_hit->weight <= 0.0)     /* if we are out of good stuff, return */
  760.       return(1);
  761.     /* fill in the rest of the hit [uwo] do this with a read...
  762.     the_best_hit->start_character = start;
  763.     the_best_hit->end_character = end;
  764.     the_best_hit->document_length = length;
  765.     the_best_hit->number_of_lines = nlines;    [uwo] handle these below */
  766.     strcpy(the_best_hit->filename,filename);
  767.     strcpy(the_best_hit->type,type);
  768.     strcpy(the_best_hit->headline,headline);
  769. /*      sprintf(the_best_hit->date, "%d", date); [uwo] handled below */
  770. /* do we need this step??  JMF -- It would seem so. Peter Marshall [uwo]. */
  771.     if (read_document_table_entry(&doc_entry, the_best_hit->document_id, db)){
  772.          the_best_hit->start_character = doc_entry.start_character;
  773.          the_best_hit->end_character = doc_entry.end_character;
  774.          the_best_hit->document_length = doc_entry.document_length;
  775.          the_best_hit->number_of_lines = doc_entry.number_of_lines;
  776.          sprintf(the_best_hit->date, "%d", doc_entry.date);
  777. /*   This doesn't seem to be necessary, though -- Peter Marshall [uwo]
  778.          read_filename_table_entry(doc_entry.filename_id, 
  779.          the_best_hit->filename,
  780.          the_best_hit->type,
  781.          NULL,
  782.          db),
  783.          strncpy(the_best_hit->headline, 
  784.          read_headline_table_entry(doc_entry.headline_id,db),
  785.          MAX_HEADLINE_LEN);
  786. [uwo] */
  787. #ifdef WIN32
  788.       /* We don't check for the existence of the file, because it might have
  789.        * a relative path name and we might not be in the right directory for
  790.        * that relative path name to work. Someone higher up can worry about
  791.        * that. */
  792.       return 0;
  793. #else
  794.       if(probe_file_possibly_compressed(the_best_hit->filename))
  795.         return(0);
  796.       else {
  797.         waislog(WLOG_HIGH, WLOG_WARNING, 
  798.                 "Dangling File %s in database %s.", 
  799.                 the_best_hit->filename,
  800.                 db->database_file);
  801.       }
  802. #endif
  803.     }
  804.     else {
  805.       waislog(WLOG_HIGH, WLOG_ERROR, 
  806.               "Error reading doc_table_entry for database %s, docid: %ld",
  807.               db->database_file,
  808.               the_best_hit->document_id);
  809.     }
  810.     beFriendly();
  811.   }
  812. }
  813.  
  814. /*-New one---------------------------------------------------------------------*/
  815.  
  816. /* this function figures out if the request is for a NEXT or Previous document.
  817.    If it is, then it makes a header for it and returns it.  If not, then it 
  818.    returns NULL. */
  819.  
  820. WAISDocumentHeader*
  821. handle_next_and_previous(docs, db, waisProtocolVersion, server)
  822. DocObj** docs;
  823. database* db;
  824. long waisProtocolVersion;
  825. char* server;
  826. {
  827.   char* dbName = db->database_file;
  828.   WAISDocumentHeader* header;
  829.   DocID* theDocID = NULL;
  830.   char *local_id;
  831.  
  832.   long count,i;
  833.   char* tmp_type = NULL;   /* temporary type */
  834.   char* tmp_type_pointer = NULL;   /* temporary type pointer */
  835.  
  836.  
  837.   if(docs != NULL) { /* All of this is for WAIS_Prev and WAIS_next */
  838.     if(docs[0] != NULL && docs[0]->Type != NULL) {
  839.       long id = -1;
  840.  
  841.       if((theDocID = docIDFromAny(docs[0]->DocumentID)) == NULL) {
  842.         waislog(WLOG_HIGH, WLOG_WARNING, "can't parse docid");
  843.         return(NULL);
  844.       }
  845.  
  846.       local_id = anyToString(GetLocalID(theDocID));
  847.  
  848.       if(strcmp(docs[0]->Type,"WAIS_NEXT") == 0)                /* next page */
  849.         id = next_docid(local_id,db);
  850.       else if(strcmp(docs[0]->Type,"WAIS_PREV") == 0)           /* prev page */
  851.         id = previous_docid(local_id, db);
  852.  
  853.       freeDocID(theDocID); s_free(local_id);
  854.  
  855.       if (id > -1) {
  856.         document_table_entry doc_entry;
  857.         hit foo;
  858.         long lines,length;
  859.         char local_id[MAX_FILENAME_LEN + 60]; /* filename, start, end */
  860.  
  861.         local_id[0] = '\0';
  862.  
  863.         if (read_document_table_entry(&doc_entry, id, db) == true) {
  864.           foo.start_character = doc_entry.start_character;
  865.           foo.end_character = doc_entry.end_character;
  866.           foo.document_length = doc_entry.document_length;
  867.           foo.number_of_lines = doc_entry.number_of_lines;
  868.  
  869.           read_filename_table_entry(doc_entry.filename_id, 
  870.                                     foo.filename,
  871.                                     foo.type,
  872.                                     NULL,
  873.                                     db),
  874.           strncpy(foo.headline, 
  875.                   read_headline_table_entry(doc_entry.headline_id,db),
  876.                   MAX_HEADLINE_LEN);
  877.           sprintf(foo.date, "%d", doc_entry.date);
  878.           sprintf(local_id, "%ld %ld %s", 
  879.                   doc_entry.start_character,
  880.                   doc_entry.end_character,
  881.                   foo.filename);
  882.                 
  883.           if(calcDocLength(&(foo),&lines,&length)){
  884.             /* this document is good, return it */
  885.             char** type = NULL;
  886.                 
  887. /* multitype extensions */
  888. /* 
  889.    Need to parse out the document types and add them to the 
  890.    document type list.
  891. */
  892.           if (waisProtocolVersion >= '2') {
  893.             
  894.             /* I left this conditional here (it is not really needed, the 
  895.                'else' part could take care of both) on the assumption that
  896.                it would be faster for single type documents.
  897.             */ 
  898.             if ( strstr(foo.type,",") == NULL ) {
  899.                type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  900.                type[0] = s_strdup(foo.type);
  901.                type[1] = NULL;
  902.             }
  903.             else {
  904.                /* count up the number of document types */
  905.                count = 1L;
  906. #ifdef WIN32
  907.                for (i = 0L; i < (long)strlen(foo.type); i++){
  908. #else
  909.                for (i = 0L; i < strlen(foo.type); i++){
  910. #endif
  911.                   if ( foo.type[i] == ',' )
  912.                      count++;
  913.                }
  914.      
  915.       
  916.                /* allocate space for types */
  917.                type = (char**)s_malloc((size_t)(sizeof(char*) * (count + 1L)));
  918.                
  919.                /* duplicate the type and save the pointer */
  920.                tmp_type = s_strdup(foo.type);
  921.                tmp_type_pointer = tmp_type;
  922.     
  923.               
  924.                /* add types - NULL out the pointer so that strtok can grab the subsequent entries */
  925.                for (i = 0L; i < count; i++ ) {
  926.                   type[i] = s_strdup(strtok(tmp_type_pointer,","));
  927.                   tmp_type_pointer = NULL;
  928.                }
  929.     
  930.                /* add the terminating null */
  931.                type[count] = NULL;
  932.                
  933.                /* release the tmp_type allocations */
  934.                s_free(tmp_type);
  935.                
  936.             }
  937.     
  938.           }
  939.                
  940.             theDocID = makeDocID();
  941.  
  942.             theDocID->distributorServer = stringToAny(server); 
  943.             theDocID->originalServer = stringToAny(server);     
  944.             theDocID->distributorDatabase = stringToAny(dbName);
  945.             theDocID->originalDatabase = stringToAny(dbName);
  946.             theDocID->distributorLocalID = stringToAny(local_id);
  947.             theDocID->originalLocalID = stringToAny(local_id);
  948.  
  949.             header=
  950.               makeWAISDocumentHeader(anyFromDocID(theDocID),
  951.                                      UNUSED,
  952.                                      -1L,
  953.                                      UNUSED,
  954.                                      length,lines,
  955.                                      type,
  956.                                      s_strdup(dbName),
  957.                                      s_strdup(foo.date),
  958.                                      s_strdup(foo.headline),
  959.                                      NULL);
  960.             freeDocID(theDocID);
  961.             return(header);
  962.           }
  963.           else{ 
  964.             waislog(WLOG_HIGH, WLOG_WARNING, 
  965.                     "document <%ld %ld %s> skipped.",
  966.                     doc_entry.start_character,
  967.                     doc_entry.end_character,
  968.                     foo.filename);
  969.             return(NULL);
  970.           }
  971.         }
  972.       }
  973.     }
  974.   }
  975.   return(NULL);
  976. }
  977.  
  978.  
  979. /*----------------------------------------------------------------------*/
  980. /* search for each of the words in a document, up to a limit.
  981.    this is for relevance feedback. */
  982.  
  983. #define MAX_TEXT_SIZE 100000    /* Maximume size of relevant text */
  984.  
  985. /* returns true if it added the words, false otherwise (not necessarily 
  986.    an error) */
  987. boolean search_for_words_in_document(doc, docid, db, diags, num_diags)
  988. DocObj* doc;
  989. long docid;
  990. database* db;
  991. diagnosticRecord*** diags;  /* list of diagnostics */
  992. long *num_diags;
  993. {
  994.   char * dbName = db->database_file;
  995.   long errorCode;
  996.   WAISDocumentText* doctext;
  997.  
  998.   char prefix[MAX_WORD_LENGTH+1];
  999.   trie *the_dict;
  1000.   trie_allocator* alloc;
  1001.   count_trie_words =0;
  1002.   count_uniq=0;
  1003.     
  1004.   alloc=make_trie_allocator();
  1005.   the_dict = new_trie("",alloc);
  1006.   *prefix = 0;
  1007.  
  1008.   if(doc->Type == NULL ||
  1009.      substrcmp(doc->Type,"TEXT") ||
  1010.      strcmp(doc->Type,"WSRC") == 0 ||
  1011.      strcmp(doc->Type,"WCAT") == 0 ||
  1012.      doc->Type[0] == 0) {
  1013.  
  1014.     doctext = NULL;
  1015.     if (doc->ChunkCode == CT_line)
  1016.       doctext = getDocumentText(doc, &errorCode, NULL);
  1017.     else if ((doc->ChunkCode == CT_byte) ||
  1018.              (doc->ChunkCode == CT_document))
  1019.       doctext = getData(doc, &errorCode, NULL);
  1020.     if (doctext != NULL) {
  1021.  
  1022.       boolean search_result;
  1023.  
  1024.       if(doctext->DocumentText->size > MAX_TEXT_SIZE)
  1025.         doctext->DocumentText->bytes[MAX_TEXT_SIZE] = 0;
  1026.       search_result = prepare_word_list(doctext->DocumentText->bytes,the_dict,alloc);  
  1027.       search_result |= search_for_trie_words(the_dict,db,prefix,docid,search_result);
  1028.       dispose_trie_allocator(alloc);
  1029.  
  1030.       freeWAISDocumentText(doctext);
  1031.       return(search_result);
  1032.     }
  1033.     else { /* bad docid? */
  1034.       DocID* theDocID = NULL;
  1035.       char* local_id = NULL;
  1036.       diagnosticRecord* diag = NULL;
  1037.       char msg[MAX_FILENAME_LEN * 2];
  1038.  
  1039.       theDocID = docIDFromAny(doc->DocumentID);
  1040.       
  1041.       if(theDocID == NULL) {
  1042.         local_id = s_strdup("can't parse docid");
  1043.       }
  1044.       else {
  1045.         local_id = anyToString(GetLocalID(theDocID));
  1046.   
  1047.         freeDocID(theDocID);
  1048.       }
  1049.       waislog(WLOG_HIGH, WLOG_WARNING,
  1050.               "Relevance Feedback with invalid doc-id: '%s'",
  1051.               local_id);
  1052.       strncpy(msg,"Relevant Document not available: ",
  1053.               MAX_FILENAME_LEN);
  1054.       s_strncat(msg,local_id,MAX_FILENAME_LEN,MAX_FILENAME_LEN);
  1055.       s_free(local_id);
  1056.       (*num_diags)++;
  1057.       diag = makeDiag(true,D_TemporarySystemError,msg);
  1058.       *diags = (diagnosticRecord**)s_realloc(*diags,(size_t)(sizeof(diagnosticRecord*) * *num_diags));
  1059.       (*diags)[(*num_diags)-1] = diag;
  1060.     }
  1061.  
  1062.   }
  1063.   return(false);
  1064. }
  1065.  
  1066.  
  1067. /*----------------------------------------------------------------------*/
  1068. #ifdef FIXDATE
  1069. FixDate(date)
  1070. char *date;
  1071. {
  1072.   long ndate;
  1073.   int year, month, day;
  1074.   ndate = atoi(date);
  1075.   year  = ndate/10000;
  1076.   month = (ndate-10000*year)/100;
  1077.   day   = (ndate-10000*year-100*month);
  1078. #ifdef DEBUG
  1079.   fprintf(stderr, "FixDate: %ld (%d/%d/%d)\n", ndate, year, month, day);
  1080.   fprintf(stderr, "FixDate: %s\n", date);
  1081. #endif
  1082.   if (year>100) strcpy(date,"0");
  1083.   else if (month>12) strcpy(date,"0");
  1084.   else if (day>31)   strcpy(date,"0");
  1085. }
  1086. #endif 
  1087.  
  1088. #ifdef ONELINELENGTH
  1089. FixLength(length)
  1090. int *length;
  1091. {
  1092.   if (*length<MAX_HEADLINE_LEN)
  1093.     *length=0;
  1094. }
  1095. #endif
  1096.  
  1097. WAISDocumentHeader*
  1098. best_hit_to_header(best_hit, maxRawScore, waisProtocolVersion, server, db)
  1099. hit* best_hit;
  1100. long maxRawScore;
  1101. long waisProtocolVersion;
  1102. char *server;
  1103. database* db;
  1104. {
  1105.   long lines,length,count,i;
  1106.   DocID* theDocID = NULL;
  1107.   WAISDocumentHeader* header;
  1108.   char* originName = db->database_file;
  1109.   char local_id[MAX_FILENAME_LEN + 60]; /* filename, start, end */
  1110.   char* tmp_type = NULL;   /* temporary type */
  1111.   char* tmp_type_pointer = NULL;   /* temporary type pointer */
  1112.   local_id[0] = '\0';
  1113.  
  1114.   if (true == calcDocLength(best_hit,&lines,&length))
  1115.     {                           /* this document is good, return it */
  1116.       char** type = NULL;
  1117.       long normalScore;
  1118.       if ( _BE_normalized )
  1119. #ifdef WIN32      
  1120.          normalScore = (long)(best_hit->weight);
  1121. #else
  1122.          normalScore = best_hit->weight;
  1123. #endif                  
  1124.       else {
  1125.          normalScore = (long)floor(
  1126.                                 (((double)best_hit->weight) /
  1127.                                  ((double)maxRawScore)) *       
  1128.                                 (MAX_NORMAL_SCORE + 1));
  1129.       
  1130.         if (normalScore > MAX_NORMAL_SCORE)
  1131.            normalScore = MAX_NORMAL_SCORE;
  1132.       }
  1133.  
  1134.       sprintf(local_id, "%ld %ld %s", 
  1135.               best_hit->start_character,
  1136.               best_hit->end_character,
  1137.               best_hit->filename);
  1138.          
  1139. /* multitype extensions */
  1140. /* 
  1141.    Need to parse out the document types and add them to the 
  1142.    document type list.
  1143. */
  1144.       if (waisProtocolVersion >= '2') {
  1145.         
  1146.         /* I left this conditional here (it is not really needed, the 
  1147.          * 'else' part could take care of both) on the assumption that
  1148.          * it would be faster for single type documents.
  1149.          */ 
  1150.         if ( strstr(best_hit->type,",") == NULL ) {
  1151.            type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1152.            type[0] = s_strdup(best_hit->type);
  1153.            type[1] = NULL;
  1154.         } else { 
  1155. /* Check for multiple types, including STELAR */
  1156. #ifdef STELAR
  1157.   /*
  1158.    * Use originName to decide whether to look for bitmaps (only database
  1159.    * 'stelar' makes associations between abstracts and bitmaps).
  1160.    */
  1161.           char *dbname;
  1162.           if ((dbname = strrchr(originName, '/')) != NULL)
  1163.             dbname++;
  1164.           else
  1165.             dbname = originName;
  1166.           if ((!strcasecmp(dbname, "STELAR") 
  1167.                || !strcasecmp(dbname, "STELAR-DB")) && BITMAPS) {
  1168.             char *absname, *bitname;
  1169.             char tmpname[80], inrec[80];
  1170.             int found = 0;
  1171.             if ((absname = strrchr(best_hit->filename, '/')) != NULL)
  1172.               strcpy(tmpname, ++absname);
  1173.             else
  1174.               strcpy(tmpname, best_hit->filename);
  1175.             absname = strtok(tmpname, ".");
  1176.             rewind(BITMAPS);
  1177.             do {
  1178.               fgets(inrec, 80, BITMAPS);
  1179.               bitname = strtok(inrec, "\n");
  1180.               if (!strcasecmp(absname, bitname)) {
  1181.                 found = 1;
  1182.                 break;
  1183.               }
  1184.             } while(!feof(BITMAPS));
  1185.  
  1186.             if (found) {
  1187.               waislog(WLOG_LOW, WLOG_RESULTS,
  1188.                       "Bitmap association: %s", absname);
  1189.               type = (char**)s_malloc((size_t)(sizeof(char*) * 4));
  1190.               type[0] = s_strdup(best_hit->type);
  1191.               type[1] = s_strdup("TIFF");
  1192.               type[2] = s_strdup("ARTICLE_TIFF");
  1193.               type[3] = NULL;
  1194.               length = 1024L*1024L;
  1195.             } else {
  1196.               type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1197.               type[0] = s_strdup(best_hit->type);
  1198.               type[1] = NULL;
  1199.             }
  1200.           } else { /* It's not the STELAR database */
  1201. #endif /* STELAR */
  1202.             /* count up the number of document types */
  1203.             count = 1L;
  1204. #ifdef WIN32
  1205.             for (i = 0L; i < (long)strlen(best_hit->type); i++){
  1206. #else
  1207.             for (i = 0L; i < strlen(best_hit->type); i++){
  1208. #endif
  1209.               if ( best_hit->type[i] == ',' )
  1210.                 count++;
  1211.             }
  1212.  
  1213.             /* allocate space for types */
  1214.             type = (char**)s_malloc((size_t)(sizeof(char*) * (count + 1L)));
  1215.    
  1216.             /* duplicate the type and save the pointer */
  1217.             tmp_type = s_strdup(best_hit->type);
  1218.             tmp_type_pointer = tmp_type;
  1219.           
  1220.             /* add types - NULL out the pointer so that strtok can grab the subsequent entries */
  1221.             for (i = 0L; i < count; i++ ) {
  1222.               type[i] = s_strdup(strtok(tmp_type_pointer,","));
  1223.               tmp_type_pointer = NULL;
  1224.             }
  1225.  
  1226.             /* add the terminating null */
  1227.             type[count] = NULL;
  1228.    
  1229.             /* release the tmp_type allocations */
  1230.             s_free(tmp_type);
  1231. #ifdef STELAR
  1232.           }
  1233. #endif /* STELAR */
  1234.         }
  1235.       }
  1236.       else
  1237.         type = NULL;
  1238.       /*
  1239.         printf("header %ld out of %ld\n", *headerNum, 
  1240.         wais_search->MaxDocumentsRetrieved); 
  1241.         */
  1242.       theDocID = makeDocID();
  1243.  
  1244.       theDocID->distributorServer = stringToAny(server);
  1245.       theDocID->originalServer = stringToAny(server);
  1246.               
  1247.       theDocID->distributorDatabase = stringToAny(originName);
  1248.       theDocID->originalDatabase = stringToAny(originName);
  1249.                   
  1250.       theDocID->distributorLocalID = stringToAny(local_id);
  1251.       theDocID->originalLocalID = stringToAny(local_id);
  1252. #ifdef FIXDATE
  1253.       FixDate(best_hit->date);
  1254. #endif
  1255. #ifdef ONELINELENGTH
  1256.       FixLength(&length);
  1257. #endif
  1258.       header =
  1259.         makeWAISDocumentHeader(anyFromDocID(theDocID),
  1260.                                UNUSED,
  1261.                                (long)normalScore,
  1262.                                best_hit->best_line,
  1263.                                length,lines,
  1264.                                type,
  1265.                                s_strdup(originName),
  1266.                                s_strdup(best_hit->date),
  1267.                                s_strdup(best_hit->headline),
  1268.                                NULL);
  1269.       freeDocID(theDocID);
  1270.       return(header);
  1271.     }
  1272.   else
  1273.     { 
  1274.       waislog(WLOG_HIGH, WLOG_WARNING, 
  1275.               "document <%ld %ld %s> skipped.",
  1276.               best_hit->start_character,
  1277.               best_hit->end_character,
  1278.               best_hit->filename);
  1279.       return(NULL);
  1280.     }
  1281. }
  1282.  
  1283.  
  1284.  
  1285. /*----------------------------------------------------------------------*/
  1286.  
  1287. boolean run_search(aSearch, headers, diags, index_directory, 
  1288.                    seed_words_used, waisProtocolVersion, headerNum)
  1289. SearchAPDU* aSearch;
  1290. WAISDocumentHeader** headers; /* list of results */
  1291. diagnosticRecord*** diags;  /* list of diagnostics */
  1292. char *index_directory;
  1293. char **seed_words_used;  /* called with enough space */
  1294. long waisProtocolVersion;
  1295. long *headerNum;
  1296. /* runs a search on the inverted file index and returns false if it errors 
  1297.    in such a way that it can not even make a diagnostic record 
  1298.    (should not happen).
  1299.    It changes headers with the replies or makes a diagnostic record
  1300.  */
  1301.   diagnosticRecord* diag = NULL;
  1302.   WAISSearch* wais_search = (WAISSearch*)aSearch->Query; /* for convenience */
  1303.   database* db = NULL;
  1304.   long maxRawScore;
  1305.   long i;
  1306.   query_parameter_type parameters;
  1307.   boolean search_result;
  1308.   char server[255];
  1309.   WAISDocumentHeader* header;
  1310.   long num_diags = 0;
  1311.   char dbName[MAX_FILENAME_LEN * 2];
  1312.  
  1313.   if (aSearch->DatabaseNames == NULL)
  1314.     strcpy(dbName,merge_pathnames(INFO_DATABASE_NAME, index_directory));
  1315.   else
  1316.     strcpy(dbName,merge_pathnames(aSearch->DatabaseNames[0], index_directory));
  1317.  
  1318. #ifdef GET_QUERY_TIMING
  1319.   ftime(&s_time);
  1320. #endif
  1321.  
  1322.   /* strlip .src if it is on the name */
  1323.   if(strlen(dbName) > strlen(".src"))
  1324.     if(0 == strcmp(dbName + strlen(dbName) - strlen(".src"),
  1325.                    ".src"))
  1326.       dbName[strlen(dbName) - strlen(".src")] = '\0';
  1327.   
  1328.   if(server_name != NULL)
  1329.     sprintf(server, "%s:%d", server_name, tcp_port);
  1330.   else
  1331.     sprintf(server, "localhost:0");
  1332.  
  1333.   db = openDatabase(dbName, false, true);
  1334.   if (db == NULL){
  1335.     char msg[MAX_FILENAME_LEN * 2];
  1336.     strncpy(msg,"The following database is not available: ",
  1337.             MAX_FILENAME_LEN);
  1338.     s_strncat(msg,dbName,MAX_FILENAME_LEN,MAX_FILENAME_LEN);
  1339.     diag = makeDiag(false,D_PermanentSystemError,msg);
  1340.     *diags = (diagnosticRecord **)s_realloc(*diags,(size_t)(sizeof(diagnosticRecord*) * 2));
  1341.     (*diags)[0] = diag;
  1342.     (*diags)[1] = NULL;
  1343.     return(false);
  1344.   }
  1345.  
  1346. #ifdef BIO              /* dgg */
  1347.   {
  1348.     char *cp= read_delimiters( db);  /* use data-specific delimiters, if available */
  1349.     if (cp != NULL) { 
  1350.       strcpy( gDelimiters, cp); 
  1351.       wordDelimiter= wordbreak_user; 
  1352.       }
  1353.     else 
  1354.       wordDelimiter= wordbreak_notalnum;
  1355.   }
  1356. #else
  1357.   wordDelimiter= wordbreak_notalnum;  /* actually, wordDelimeter is used only ifdef BIO ? */
  1358. #endif
  1359.  
  1360.   /* figure out if it is a NEXT or PREVIOUS, if so, return it. */
  1361.   header = handle_next_and_previous(wais_search->Docs, db, 
  1362.                                     waisProtocolVersion, server);
  1363.   if(header != NULL){
  1364.     headers[(*headerNum)++] = header;
  1365.     headers[*headerNum] = NULL;
  1366.     return(true);
  1367.   }
  1368.   
  1369.   /* until seed_words_used is supported */
  1370. /*  strcpy(*seed_words_used, wais_search->SeedWords); */
  1371.  
  1372.   if (seed_words_used != NULL) *seed_words_used[0] = 0;
  1373.  
  1374.   parameters.max_hit_retrieved = wais_search->MaxDocumentsRetrieved;
  1375.   set_query_parameter(SET_MAX_RETRIEVED_MASK, ¶meters);
  1376.  
  1377.   search_result = false;
  1378.   init_search_word(db);
  1379.  
  1380. #ifdef RELEVANCE_FEEDBACK
  1381.   if(wais_search->Docs != NULL) {
  1382.     DocObj* doc = NULL;
  1383.     boolean res;
  1384.     /* assemble the elements and construct a response */
  1385.     for (i = 0, doc = wais_search->Docs[i]; 
  1386.          doc != NULL; 
  1387.          doc = wais_search->Docs[++i]){
  1388.       search_result |= 
  1389.         search_for_words_in_document(doc,i+1,db,diags,&num_diags);
  1390.     }
  1391.     if (*diags != NULL) {
  1392.         num_diags++;
  1393.         *diags = (diagnosticRecord**)s_realloc(*diags,(size_t)(sizeof(diagnosticRecord*) * num_diags));
  1394.         (*diags)[num_diags-1] = NULL;
  1395.       }
  1396.   }
  1397. #endif                          /* RELEVANT_FEEDBACK */
  1398.  
  1399.   search_result |= search_for_words(wais_search->SeedWords, db, 0, *seed_words_used);
  1400.  
  1401.   if (search_result == true){ /* the search went ok */
  1402.       hit best_hit;
  1403.       finished_search_word(db);
  1404.       init_best_hit(db);
  1405.       for (i = 0; i < wais_search->MaxDocumentsRetrieved; i++){ 
  1406.         if(0 != next_best_hit(&best_hit, db))
  1407.           break;                /* out of hits */
  1408.         if(i == 0)
  1409. #ifdef WIN32
  1410.           maxRawScore = (long)(best_hit.weight);
  1411. #else       
  1412.           maxRawScore = best_hit.weight;
  1413. #endif          
  1414.         if (best_hit.weight > 0){
  1415.           WAISDocumentHeader* header = 
  1416.             best_hit_to_header(&best_hit, maxRawScore,
  1417.                                waisProtocolVersion,server,db);
  1418.           if(NULL != header){
  1419.             headers[(*headerNum)++] = header;
  1420.             headers[*headerNum] = NULL;
  1421.           }
  1422.         }
  1423.       }
  1424.     }
  1425.   else
  1426.     {                           /* something went awry in the search */
  1427.       num_diags++;
  1428.       diag = makeDiag(true,D_PermanentSystemError,
  1429.                       "Serious error in server");
  1430.       *diags = (diagnosticRecord**)
  1431.         s_realloc(*diags, (size_t)(sizeof(diagnosticRecord*) * num_diags));
  1432.       (*diags)[num_diags-2] = diag;
  1433.       (*diags)[num_diags-1] = NULL;
  1434.     }
  1435.   finished_best_hit(db);
  1436.   /* free everything */
  1437.   closeDatabase(db);
  1438. #ifdef GET_QUERY_TIMING
  1439.   ftime(&e_time);
  1440.   t_time += (e_time.time + e_time.millitm/1000.0) - 
  1441.             (s_time.time + s_time.millitm/1000.0);
  1442.   n_query++;
  1443.   if ( n_query == 200 ) {
  1444.    waislog(WLOG_LOW, WLOG_INFO, "searching 200 queries takes %f seconds.",
  1445.            t_time);
  1446.    waislog(WLOG_LOW, WLOG_INFO, "average %f/query.", t_time/200.0);
  1447.    n_query = 0;
  1448.    t_time = 0;
  1449.    }
  1450. #endif
  1451.  
  1452.   return(true);
  1453. }
  1454.